/*
 * @Author: your name
 * @Date: 2020-06-18 15:26:18
 * @LastEditTime: 2020-06-19 13:29:51
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \DepView\src\main\java\me\nettee\depview\model\DepGraph.java
 */ 
package me.nettee.depview.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.graph.MutableNetwork;
import com.google.common.graph.NetworkBuilder;

import org.apache.commons.lang3.StringUtils;

import me.nettee.depview.main.Env;

public class DepGraph {

    private final Env env;
    private MutableNetwork<PlainClass, DepAttr> depGraph;
    private Map<PlainClass, Integer> classSizes;
    private int allDepCount = 0;

    public DepGraph(Env env) {
        this.env = env;
        this.depGraph = NetworkBuilder.directed()
                .allowsParallelEdges(true)
                .allowsSelfLoops(true)
                .build();
        this.classSizes = new HashMap<>();
    }

    public void addDep(Dep<? extends DepAttr> invDep) {
        PlainClass fromClass = invDep.getFromClass();
        PlainClass toClass = invDep.getToClass();

        allDepCount++;

        if (!fromClass.isInPackage(env.getProjectPackage())) {
            return;
        }

        if (!toClass.isInPackage(env.getProjectPackage())) {
            return;
        }

        depGraph.addNode(fromClass);
        depGraph.addNode(toClass);

        depGraph.addEdge(fromClass, toClass, invDep.getAttr());
    }

    public void setClassSize(PlainClass class_, int size) {
        classSizes.put(class_, size);
    }

    public void santinize() {
        List<PlainClass> nodesToRemove = new ArrayList<>();
        for (PlainClass node : depGraph.nodes()) {
            if (!classSizes.containsKey(node)) {
                System.out.printf("invalid node %s, remove it\n", node.toString());
                nodesToRemove.add(node);
            }
        }
        for (PlainClass node : nodesToRemove) {
            depGraph.removeNode(node);
        }
    }

    public void printDependencies() {

        Set<PlainClass> classes = depGraph.nodes();
        Set<DepAttr> dependencies = depGraph.edges();

        System.out.printf("Statistics:\n");
        System.out.printf("\t %d project classes\n", classes.size());
        System.out.printf("\t %d invocation dependencies between project classes (out of %d)\n",
                    dependencies.size(), allDepCount);
        
    }

    private static String padTo(String s, int len) {
        if (s.length() >= len) {
            return s;
        } else {
            return s + StringUtils.repeat(' ', len - s.length());
        }
    }

    public MutableNetwork<PlainClass, DepAttr> getGraph() {
        return depGraph;
    }

    public Map<PlainClass, Integer> getClassSizes() {
        return classSizes;
    }
}

